﻿using System;
using System.Collections.Generic;
using System.Text;
using btom = Microsoft.BizTalk.ExplorerOM;
using HenIT.Utilities;

//Important note. 
//This app must be compiled for 32bit execution only since BTS Explorer OM does not support 64bit 
//Yes I know, this sucks

namespace HenIT.BizTalk
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0 || CommandLineUtils.IsCommand(args, "/?", "-?", "/h", "-h"))
                ShowHelp();
            else
            {
                string trackingOptionsFilePath = CommandLineUtils.GetDefault(args, "");
                string sqlServer = CommandLineUtils.GetCommand(args, BizTalkUtils.GetCurrentBizTalkSqlServer() , "-s:", "/s:");
                string bizTalkMgmtDb = CommandLineUtils.GetCommand(args, BizTalkUtils.GetCurrentBizTalkMgmtDb(), "-d:", "/d:");
                if (trackingOptionsFilePath.Length != 0 && System.IO.File.Exists(trackingOptionsFilePath))
                {
                    TrackingOptions trackingOptions = new TrackingOptions(trackingOptionsFilePath);
                    using (btom.BtsCatalogExplorer catalog = new btom.BtsCatalogExplorer())
                    {
                        catalog.ConnectionString = "SERVER=" + sqlServer + ";DATABASE=" + bizTalkMgmtDb + ";Integrated Security=SSPI";

                        Console.WriteLine("Setting tracking options for");
                        foreach (Application application in trackingOptions.Applications)
                        {
                            try
                            {
                                SetApplicationsTrackingTypes(catalog, application);
                            }
                            catch (Exception ex) //handle exceptions per app
                            {
                                Console.WriteLine(ex.Message);
                            }
                        }
                        catalog.SaveChanges();
                    }
                }
                else
                {
                    Console.WriteLine("Invalid input file specified!");
                }
            }
#if DEBUG
            Console.WriteLine("Done");
            System.Threading.Thread.Sleep(3000);
#endif            
        }

        #region Set tracking types

        #region SetApplicationsTrackingTypes
        private static void SetApplicationsTrackingTypes(btom.BtsCatalogExplorer catalog, Application application)
        {
            if (application.Name == "*")
            {
                foreach (btom.Application btsApplication in catalog.Applications)
                {
                    SetApplicationTrackingTypes(btsApplication, application);
                }
            }
            else if (catalog.Applications[application.Name] == null)
            {
                throw new Exception("Application " + application.Name + " does not exist in BizTalk group ");
            }
            else
            {
                SetApplicationTrackingTypes(catalog.Applications[application.Name], application);
            }
        }
        private static void SetApplicationTrackingTypes(btom.Application btsApplication, Application application)
        {
            if (btsApplication.IsSystem)
            {
                Console.WriteLine("  Application: " + btsApplication.Name);
                Console.WriteLine("     This is a 'System' Application. Only pipelines options can be set.");
                Console.WriteLine("    Pipelines");
                SetPipelinesTrackingTypes(btsApplication, application);
            }
            else
            {
                Console.WriteLine("  Application: " + btsApplication.Name);
                Console.WriteLine("    Receive ports");
                SetReceivePortsTrackingTypes(btsApplication, application);
                Console.WriteLine("    Send ports");
                SetSendPortsTrackingTypes(btsApplication, application);
                Console.WriteLine("    Orchestrations");
                SetOrchestrationsTrackingTypes(btsApplication, application);
                Console.WriteLine("    Pipelines");
                SetPipelinesTrackingTypes(btsApplication, application);
            }
            Console.WriteLine();
        }
        #endregion

        #region SetReceivePortsTrackingTypes
        private static void SetReceivePortsTrackingTypes(btom.Application btsApplication, Application application)
        {
            foreach (ReceivePort receivePort in application.ReceivePorts)
            {
                if (receivePort.Name == "*")
                {
                    foreach (btom.ReceivePort btsReceivePort in btsApplication.ReceivePorts)
                    {
                        SetReceivePortTrackingTypes(btsReceivePort, receivePort);
                    }
                }
                else
                {
                    if (btsApplication.ReceivePorts[receivePort.Name] == null)
                    {
                        throw new Exception("Receive port " + receivePort.Name + " does not exist in application " + application.Name);
                    }
                    else
                    {
                        SetReceivePortTrackingTypes(btsApplication.ReceivePorts[receivePort.Name], receivePort);
                    }
                }
            }
        }
        private static void SetReceivePortTrackingTypes(btom.ReceivePort btsReceivePort, ReceivePort receivePort)
        {
            foreach (btom.TrackingTypes removeOption in receivePort.RemoveOptions)
            {
                if (IsTrackingTypes(btsReceivePort.Tracking, removeOption))
                    btsReceivePort.Tracking = btsReceivePort.Tracking ^ removeOption;
            }
            foreach (btom.TrackingTypes addOption in receivePort.AddOptions)
            {
                if (btsReceivePort.IsTwoWay ||
                    (!btsReceivePort.IsTwoWay &&
                        (IsTrackingTypes(btom.TrackingTypes.AfterReceivePipeline, addOption) ||
                         IsTrackingTypes(btom.TrackingTypes.BeforeReceivePipeline, addOption) ||
                         IsTrackingTypes(btom.TrackingTypes.TrackPropertiesAfterReceivePipeline, addOption) ||
                         IsTrackingTypes(btom.TrackingTypes.TrackPropertiesBeforeReceivePipeline, addOption)
                        )
                     )
                   )
                    btsReceivePort.Tracking = btsReceivePort.Tracking | addOption;
            }
        } 
        #endregion

        #region SetSendPortsTrackingTypes
        private static void SetSendPortsTrackingTypes(btom.Application btsApplication, Application application)
        {
            foreach (SendPort sendPort in application.SendPorts)
            {
                if (sendPort.Name == "*")
                {
                    foreach (btom.SendPort btsSendPort in btsApplication.SendPorts)
                    {
                        SetSendPortTrackingTypes(btsSendPort, sendPort);
                    }
                }
                else
                {
                    if (btsApplication.SendPorts[sendPort.Name] == null)
                    {
                        throw new Exception("Send port " + sendPort.Name + " does not exist in application " + application.Name);
                    }
                    else
                    {
                        SetSendPortTrackingTypes(btsApplication.SendPorts[sendPort.Name], sendPort);
                    }
                }
            }
        }
        private static void SetSendPortTrackingTypes(btom.SendPort btsSendPort, SendPort sendPort)
        {
            foreach (btom.TrackingTypes removeOption in sendPort.RemoveOptions)
            {
                if (IsTrackingTypes(btsSendPort.Tracking, removeOption))
                    btsSendPort.Tracking = btsSendPort.Tracking ^ removeOption;
            }
            foreach (btom.TrackingTypes addOption in sendPort.AddOptions)
            {
                if (btsSendPort.IsTwoWay ||
                    (!btsSendPort.IsTwoWay &&
                        (IsTrackingTypes(btom.TrackingTypes.AfterSendPipeline, addOption) ||
                         IsTrackingTypes(btom.TrackingTypes.BeforeSendPipeline, addOption) ||
                         IsTrackingTypes(btom.TrackingTypes.TrackPropertiesAfterSendPipeline, addOption) ||
                         IsTrackingTypes(btom.TrackingTypes.TrackPropertiesBeforeSendPipeline, addOption)
                        )
                     )
                   )
                    btsSendPort.Tracking = btsSendPort.Tracking | addOption;
            }
        } 
        #endregion

        #region SetOrchestrationsTrackingTypes
        private static void SetOrchestrationsTrackingTypes(btom.Application btsApplication, Application application)
        {
            foreach (Orchestration orchestration in application.Orchestrations)
            {
                if (orchestration.Name == "*")
                {
                    foreach (btom.BtsOrchestration btsOrchestration in btsApplication.Orchestrations)
                    {
                        SetOrchestrationTrackingTypes(btsOrchestration, orchestration);
                    }
                }
                else
                {
                    if (btsApplication.Orchestrations[orchestration.Name] == null)
                    {
                        throw new Exception("Orchestration " + orchestration.Name + " does not exist in application " + application.Name);
                    }
                    else
                    {
                        SetOrchestrationTrackingTypes(btsApplication.Orchestrations[orchestration.Name], orchestration);
                    }
                }
            }
        }
        private static void SetOrchestrationTrackingTypes(btom.BtsOrchestration btsOrchestration, Orchestration orchestration)
        {
            //remove must be done in reverse order since some options depends on others.
            for (int i = orchestration.RemoveOptions.Count - 1; i >= 0; i--)
            {
                if (IsTrackingTypes(btsOrchestration.Tracking, orchestration.RemoveOptions[i]))
                    btsOrchestration.Tracking = btsOrchestration.Tracking ^ orchestration.RemoveOptions[i];
            }
            foreach (btom.OrchestrationTrackingTypes addOption in orchestration.AddOptions)
            {
                btsOrchestration.Tracking = btsOrchestration.Tracking | addOption;
            }
        } 
        #endregion

        #region SetPipelinesTrackingTypes
        private static void SetPipelinesTrackingTypes(btom.Application btsApplication, Application application)
        {
            foreach (Pipeline pipeline in application.Pipelines)
            {
                if (pipeline.Name == "*")
                {
                    foreach (btom.Pipeline btsPipeline in btsApplication.Pipelines)
                    {
                        SetPipelineTrackingTypes(btsPipeline, pipeline);
                    }
                }
                else
                {
                    if (btsApplication.Pipelines[pipeline.Name] == null)
                    {
                        throw new Exception("Pipeline " + pipeline.Name + " does not exist in application " + application.Name);
                    }
                    else
                    {
                        SetPipelineTrackingTypes(btsApplication.Pipelines[pipeline.Name], pipeline);
                    }
                }
            }
        }
        private static void SetPipelineTrackingTypes(btom.Pipeline btsPipeline, Pipeline pipeline)
        {
            //remove must be done in reverse order since some options depends on others.
            for (int i = pipeline.RemoveOptions.Count - 1; i >= 0; i--)
            {
                if (IsTrackingTypes(btsPipeline.Tracking, pipeline.RemoveOptions[i]))
                    btsPipeline.Tracking = btsPipeline.Tracking ^ pipeline.RemoveOptions[i];
            }
            foreach (btom.PipelineTrackingTypes addOption in pipeline.AddOptions)
            {
                btsPipeline.Tracking = btsPipeline.Tracking | addOption;
            }
        } 
        #endregion

        #endregion

        static void ShowHelp()
        {
            Console.WriteLine("Usage: BTSSetTrackingOptions.exe <TrackingOptionsFile> [-s:server] [-d:database]");
            Console.WriteLine(" where");
            Console.WriteLine("   TrackingOptionsFile : xml file containing tracking options");
            Console.WriteLine("   -s : Sql server");
            Console.WriteLine("   -d : Management database e.g. BizTalkMgmtDb");
        }

        #region IsTrackingTypes
        private static bool IsTrackingTypes(btom.TrackingTypes tracking, btom.TrackingTypes compareTo)
        {
            return ((tracking & compareTo) == compareTo);
        }
        private static bool IsTrackingTypes(btom.OrchestrationTrackingTypes tracking, btom.OrchestrationTrackingTypes compareTo)
        {
            return ((tracking & compareTo) == compareTo);
        }
        private static bool IsTrackingTypes(btom.PipelineTrackingTypes tracking, btom.PipelineTrackingTypes compareTo)
        {
            return ((tracking & compareTo) == compareTo);
        }
        #endregion
    }
}
